# 手动的导入serializers类 自动的检测不到这个serializers类 因为它是rest_framework里面的类
from rest_framework import serializers

from drfstudy.models import Student, Classes

"""
序列化器类定义好了之后就可以指定哪些字段 哪些数据需要转换以及去做数据的检验：
  1.对用户提交的数据进行检验 前端转后端也叫反序例化
  2.对要返回的数据（模型对象）进行指定的转换（转换成json格式的数据） 后端转前端也叫序列化  
"""

# 定义一个学生的序列化器的类 让它继承serializers.Serializer序列化器成为一个序列化器类
# class StudentSerializer(serializers.Serialize):
#     # id不要忘记了 虽然定义模型类的时候没有去写 因为模型类会帮我们自动的去生成id 这里需要我们自己去写
#     # 定义序列化器类的时候就和定义模型类的时候是差不多的 所以我们可以把模型类放到旁边跟着它来定义跟着它来写
#     # id是什么数据类型用序列化器转换的时候就用什么类型的字段来转换
#     # label='ID'设置标签的名字为大小的ID   read_only=True 把它设置不能被修改的 让它自动的去生成
#     id = serializers.IntegerField(label='ID', read_only=True)
#     # 名字是字符串类型 就用字符串类型的字段来转换 给它个最长的字符个数设置
#     name = serializers.CharField(max_length=30, label="姓名")
#     # 年龄是整数类型 也用整型字段来转换  required=False不是必填项 因为它会去帮我们去校验 所以这里限制一下它不是必填项
#     age = serializers.IntegerField(required=False, label="年龄")
#     # 性别一般是用：1代表男 2代表女 是整数类型所以用整型字段来转换   required=True默认可以为空的
#     sex = serializers.IntegerField(required=True, label="性别")
#     # 创建时间用时间类型字段来转换
#     # create_time = serializers.DateTimeField(required=False, label="创建时间")
#     # # 修改时间也用时间类型字段来转换
#     # update_time = serializers.DateTimeField(required=False, label="修改时间")
#     # # 逻辑删除用布尔类型字段来转换  default=False 默认值是False
#     # is_delete = serializers.BooleanField(required=False, default=False, label="逻辑删除")
#
#     # 在序列化器类中在定义一个create方法 因为继承的父类的序列器的create方法，没有做任何的序列化保存操作，不满足持久化数据的要求，需要重写
#     # validated_data前端传入的数据，一个字典
#     def create(self, validated_data):
#         # 返回调用这个Student模型类的创建的方法 让它来保存create方法把前端传过来的数据放里面 因为它是个字典所以要用不定长的形式传递进去的
#         return Student.objects.create(**validated_data)
#
#     # instance 实例对象 查询到的表数据， 例如查询到的id为1的学生
#     # validated_data 新数据 前端传过来的数据 类字典
#     def update(self, instance, validated_data):
#         # [键]取值方式 如果不存在会报错
#         # get方法可以自定义返回的值，键有对应的值，则返回值，如果没有则返回后面自定义的内容
#         # 获取name键对应的值，如果有的话代表修改了用户名，得到新的用户名
#         # 获取name键对应的值，如果没有则代表没有修改用户名，使用对象数据表里面原本的值
#         instance.name = validated_data.get("name", instance.name)
#         instance.age = validated_data.get("age", instance.age)
#         instance.sex = validated_data.get("sex", instance.sex)
#         # 通过模型对象做保存的操作 持久化数据
#         instance.save()
#         # 返回表数据对象
#         return instance

# 定义一个学生序列化器类
class StudentSerializer(serializers.ModelSerializer):
    # classes.name
    # 把模型的属性值作为来源
    # 添加额外的字段   指定资源来自于：source='classes.name'     read_only=True只能查看 不能修改
    # 只读参数：read_only=True 不能取消不写 否则会报错 序列化器在获取class_name的值时候 就会遇到获取值的报错
    # 它从学生对象中获取到的序列化器字段可能是错误的命令 或者说student对象上没有匹配到属性和键
    # Got AttributeError when attempting to get a value for field `class_name` on serializer `StudentSerializer`.
    # The serializer field might be named incorrectly and not match any attribute or key on the `Student` instance.
    # Original exception text was: 'NoneType' object has no attribute 'name'.
    class_name = serializers.CharField(source='classes.name', read_only=True)

    # Meta 元数据类 我们可以在这个类里面去配置序列化器的元数据
    # 也就是说在这个Meta的类里面 可以告诉StudentSerializer序列化器类 该怎么工作 要处理哪些数据
    # 数据来源模型是那个模型 一些基本的数据校验可以写在Meta里面
    # 定义一个内部类 也就是嵌套一个类
    # 它主要负责定义序列化器和模型之间的映射关系 以及控制字段的行为 比如：字段长度的检验 只读或者只写的检验
    class Meta:

        # 声明一下 这个模型来自于 模型文件：models.py 里面的学生模型类 对这个学生的模型类进行序列化的操作
        model = Student

        # 指定映射的字段，映射所有的字段，都会展示出来
        # fields = '__all__'

        # 指定只展示该两个字段给前端显示
        # fields = ["name", "age"]

        # 如果字段和内容非常多 你想要给别人看的字段也非常多 而其中不需要给别人看的字段只有两个的时候
        # 除去该字段不展示，其他的都在前端展示    在后端显示的加映射
        exclude = ["is_delete"]

        # 添加额外的数据校验
        extra_kwargs = {
            "age": {
                'min_value': 1,
                'max_value': 150,
            },

            # 限制班级为只写操作 就不能去做读取的操作了
            'classes': {'write_only': True}
        }

    # 关于方法的检验 和多字段的检验要写在Meta类的外面
    # 自动将用户传入的字段值做为参数传入到该页面
    # validate_ 你要检验的字段名
    # 属性级别的检验 只能检验一个字段
    # def validate_name(self, value):
    #     if 'drf' not in value.lower():
    #     # if 'drf' not in attrs.get('name').lower():
    #         raise serializers.ValidationError("用户名需要包含drf")
    #     return value

    # 要检验多个字段  就要针对一条数据 一条数据就是模型类里面的一个对象 就是对象级别的检验
    # attrs 会把用户提交的所有数据传入到参数 并且字典类型 它可以一次性对多个字段进行检验 也叫联动检验
    def validate(self, attrs):
        if 'drf' not in attrs.get("name").lower():
            raise serializers.ValidationError("用户名需要包含drf")
        if attrs.get("age") < 18:
            raise serializers.ValidationError("未成年人禁止使用")
        return attrs
    # 这两个检验的方法所做的操作都是反序列化转换的操作 它们都是对前端提交的数据进行内容格式的检验

# 不想再这个班级里面显示所有的学生 对上面的学生序列化器类做一个简化
class StudentSerializerSimple(serializers.ModelSerializer):
    # 定义一个内部类 也就是嵌套一个类
    class Meta:
        # 指定源数据类model里面的数据 来自与Student类
        model = Student
        # 指定显示的字段名字
        fields = ["id", "name", "age", "sex"]

# 定义班级序列化器类
class ClassesSerializer(serializers.ModelSerializer):
    # 添加一个额外的字段
    # 属性名就是管理器的名字
    # 把该班级的所有学生使用 StudentSerializer序列化器，进行序列化的操作
    # 用student_set保存
    # 序列化器的嵌套 在一个序列化器里面使用另一个序列化器
    # student_set = StudentSerializer(many=True, read_only=True)
    student_set = StudentSerializerSimple(many=True, read_only=True)

    # 定义一个内部类 也就是嵌套一个类
    class Meta:
        # 声明一下 这个模型来自于 模型文件：models.py 里面的班级模型类 对这个班级的模型类进行序列化的操作
        # 需要导入一下 因为它不是在这个文件里面定义的
        model = Classes
        exclude = ["is_delete"]








